1 using System.Collections.Generic;
2 using UnityEngine;
3
4 namespace ProceduralToolkit.Examples
5 {
6 public abstract class ProceduralFacadePanel : IFacadePanel
7 {
8 public Vector2? origin { get; set; }
9 public float? width { get; set; }
10 public float? height { get; set; }
11
12 private const float SocleWindowWidth = 0.7f;
13 private const float SocleWindowHeight = 0.4f;
14 private const float SocleWindowDepth = 0.1f;
15 private const float SocleWindowHeightOffset = 0.1f;
16
17 private const float EntranceDoorWidth = 1.8f;
18 private const float EntranceDoorHeight = 2;
19 private const float EntranceDoorThickness = 0.05f;
20
21 private const float EntranceRoofLength = 1;
22 private const float EntranceRoofHeight = 0.15f;
23
24 private const float EntranceWindowWidthOffset = 0.4f;
25 private const float EntranceWindowHeightOffset = 0.3f;
26
27 private const float WindowDepth = 0.1f;
28 private const float WindowWidthOffset = 0.5f;
29 private const float WindowBottomOffset = 1;
30 private const float WindowTopOffset = 0.3f;
31 private const float WindowFrameWidth = 0.05f;
32 private const float WindowSegmentMinWidth = 0.9f;
33
34 private const float BalconyHeight = 1;
35 private const float BalconyDepth = 0.8f;
36 private const float BalconyThickness = 0.1f;
37
38 private const float AtticHoleWidth = 0.3f;
39 private const float AtticHoleHeight = 0.3f;
40 private const float AtticHoleDepth = 0.5f;
41
42 public abstract MeshDraft GetMeshDraft();
43
44 protected static MeshDraft PerforatedQuad(
45 Vector3 min,
46 Vector3 max,
47 Vector3 innerMin,
48 Vector3 innerMax)
49 {
50 Vector3 size = max - min;
51 Vector3 widthVector = size.OnlyXZ();
52 Vector3 heightVector = size.OnlyY();
53 Vector3 normal = Vector3.Cross(heightVector, widthVector).normalized;
54
55 Vector3 innerSize = innerMax - innerMin;
56 Vector3 innerHeight = innerSize.OnlyY();
57 Vector3 innerWidth = innerSize.OnlyXZ();
58
59 Vector3 vertex0 = min;
60 Vector3 vertex1 = min + heightVector;
61 Vector3 vertex2 = max;
62 Vector3 vertex3 = min + widthVector;
63 Vector3 window0 = innerMin;
64 Vector3 window1 = innerMin + innerHeight;
65 Vector3 window2 = innerMax;
66 Vector3 window3 = innerMin + innerWidth;
67
68 return new MeshDraft
69 {
70 vertices = new List<Vector3>(8)
71 {
72 vertex0,
73 vertex1,
74 vertex2,
75 vertex3,
76 window0,
77 window1,
78 window2,
79 window3,
80 },
81 normals = new List<Vector3>(8) {normal, normal, normal, normal, normal, normal, normal, normal},
82 triangles = new List<int>(24) {0, 1, 4, 4, 1, 5, 1, 2, 5, 5, 2, 6, 2, 3, 6, 6, 3, 7, 3, 0, 7, 7, 0, 4,}
83 };
84 }
85
86 public static MeshDraft Window(
87 Vector3 origin,
88 float width,
89 float height,
90 Color wallColor,
91 Color frameColor,
92 Color glassColor)
93 {
94 return Window(origin, width, height, WindowWidthOffset, WindowBottomOffset, WindowTopOffset, wallColor,
95 frameColor, glassColor);
96 }
97
98 protected static MeshDraft Window(
99 Vector3 min,
100 float width,
101 float height,
102 float widthOffset,
103 float bottomOffset,
104 float topOffset,
105 Color wallColor,
106 Color frameColor,
107 Color glassColor)
108 {
109 Vector3 widthVector = Vector3.right*width;
110 Vector3 heightVector = Vector3.up*height;
111 Vector3 max = min + widthVector + heightVector;
112 Vector3 frameMin = min + Vector3.right*widthOffset + Vector3.up*bottomOffset;
113 Vector3 frameMax = max - Vector3.right*widthOffset - Vector3.up*topOffset;
114 Vector3 frameWidth = Vector3.right*(width - widthOffset*2);
115 Vector3 frameHeight = Vector3.up*(height - bottomOffset - topOffset);
116 Vector3 frameDepth = Vector3.forward*WindowDepth;
117 Vector3 frameSize = frameMax - frameMin;
118
119 var draft = PerforatedQuad(min, max, frameMin, frameMax);
120
121 var frame = MeshDraft.PartialBox(frameWidth, -frameDepth, frameHeight,
122 Directions.All & ~Directions.ZAxis);
123 frame.Move(frameMin + frameSize/2 + frameDepth/2);
124 draft.Add(frame);
125 draft.Paint(wallColor);
126
127 draft.Add(Windowpane(frameMin + frameDepth, frameMax + frameDepth, frameColor, glassColor));
128
129 return draft;
130 }
131
132 private static MeshDraft Windowpane(Vector3 min, Vector3 max, Color frameColor, Color glassColor)
133 {
134 Vector3 size = max - min;
135 Vector3 widthVector = size.OnlyXZ();
136 Vector3 heightVector = size.OnlyY();
137 Vector3 right = widthVector.normalized;
138 Vector3 normal = Vector3.Cross(widthVector, heightVector).normalized;
139 var draft = new MeshDraft();
140
141 int rodCount = Mathf.FloorToInt(widthVector.magnitude/WindowSegmentMinWidth);
142 float interval = widthVector.magnitude/(rodCount + 1);
143
144 Vector3 frameWidth = right*WindowFrameWidth/2;
145 Vector3 frameHeight = Vector3.up*WindowFrameWidth/2;
146 Vector3 frameLength = normal*WindowFrameWidth/2;
147 Vector3 startPosition = min + heightVector/2 + frameLength/2;
148 for (int i = 0; i < rodCount; i++)
149 {
150 var frame = MeshDraft.PartialBox(frameWidth*2, frameLength, heightVector - frameHeight*2,
151 Directions.Left | Directions.Back | Directions.Right);
152 frame.Move(startPosition + right*(i + 1)*interval);
153 draft.Add(frame);
154 }
155
156 Vector3 windowMin = min + frameWidth + frameHeight;
157 Vector3 windowWidth = widthVector - frameWidth*2;
158 Vector3 windowHeight = heightVector - frameHeight*2;
159 Vector3 windowMax = windowMin + windowWidth + windowHeight;
160 var window = PerforatedQuad(min, max, windowMin, windowMax);
161 draft.Add(window);
162
163 var hole = MeshDraft.PartialBox(windowWidth, frameLength, windowHeight, Directions.All & ~Directions.ZAxis);
164 hole.Move(startPosition + widthVector/2);
165 hole.FlipFaces();
166 draft.Add(hole);
167 draft.Paint(frameColor);
168
169 var glass = MeshDraft.Quad(windowMin + frameLength, windowWidth, windowHeight);
170 glass.Paint(glassColor);
171 draft.Add(glass);
172
173 return draft;
174 }
175
176 protected static MeshDraft Balcony(
177 Vector3 origin,
178 float width,
179 float height,
180 Color wallColor,
181 Color frameColor,
182 Color glassColor)
183 {
184 Vector3 widthVector = Vector3.right*width;
185 Vector3 heightVector = Vector3.up*height;
186
187 var draft = Balcony(origin, width, wallColor);
188
189 Vector3 innerHeightOffset = Vector3.up*BalconyThickness;
190
191 Vector3 windowHeightOffset = Vector3.up*WindowBottomOffset;
192 Vector3 windowWidth = Vector3.right*(width - WindowWidthOffset*2);
193 Vector3 windowHeight = Vector3.up*(height - WindowBottomOffset - WindowTopOffset);
194 Vector3 windowDepth = Vector3.forward*WindowDepth;
195
196 int rodCount = Mathf.FloorToInt(windowWidth.magnitude/WindowSegmentMinWidth);
197 Vector3 doorWidth = Vector3.right*windowWidth.magnitude/(rodCount + 1);
198 Vector3 doorHeight = windowHeightOffset + windowHeight;
199
200 Vector3 outerFrameOrigin = origin + Vector3.right*WindowWidthOffset;
201 List<Vector3> outerFrame = new List<Vector3>
202 {
203 outerFrameOrigin + innerHeightOffset,
204 outerFrameOrigin + doorHeight,
205 outerFrameOrigin + windowWidth + doorHeight,
206 outerFrameOrigin + windowWidth + windowHeightOffset,
207 outerFrameOrigin + doorWidth + windowHeightOffset,
208 outerFrameOrigin + doorWidth + innerHeightOffset
209 };
210
211 var panel = MeshDraft.TriangleStrip(new List<Vector3>
212 {
213 outerFrame[0],
214 origin,
215 outerFrame[1],
216 origin + heightVector,
217 outerFrame[2],
218 origin + widthVector + heightVector,
219 outerFrame[3],
220 origin + widthVector,
221 outerFrame[4],
222 origin + widthVector,
223 outerFrame[5]
224 });
225 panel.Paint(wallColor);
226
227 draft.Add(panel);
228
229 List<Vector3> innerFrame = new List<Vector3>();
230 foreach (Vector3 vertex in outerFrame)
231 {
232 innerFrame.Add(vertex - windowDepth);
233 }
234 var frame = MeshDraft.FlatBand(innerFrame, outerFrame);
235 frame.Paint(wallColor);
236 draft.Add(frame);
237
238 Vector3 windowpaneMin1 = outerFrame[0] - windowDepth;
239 Vector3 windowpaneMin2 = outerFrame[4] - windowDepth;
240 draft.Add(Windowpane(windowpaneMin1, windowpaneMin1 + doorWidth + doorHeight - innerHeightOffset,
241 frameColor, glassColor));
242 draft.Add(Windowpane(windowpaneMin2, windowpaneMin2 + windowWidth - doorWidth + windowHeight,
243 frameColor, glassColor));
244
245 return draft;
246 }
247
248 private static MeshDraft Balcony(Vector3 origin, float width, Color wallColor)
249 {
250 Vector3 widthVector = Vector3.right*width;
251 Vector3 balconyHeight = Vector3.up*BalconyHeight;
252 Vector3 balconyDepth = Vector3.back*BalconyDepth;
253
254 var balconyOuter = MeshDraft.PartialBox(widthVector, balconyDepth, balconyHeight,
255 Directions.All & ~Directions.Up & ~Directions.Back);
256 balconyOuter.FlipFaces();
257 Vector3 balconyCenter = origin + widthVector/2 + balconyDepth/2 + balconyHeight/2;
258 balconyOuter.Move(balconyCenter);
259
260 Vector3 innerWidthOffset = Vector3.right*BalconyThickness;
261 Vector3 innerWidth = widthVector - innerWidthOffset*2;
262 Vector3 innerHeightOffset = Vector3.up*BalconyThickness;
263 Vector3 innerHeight = balconyHeight - innerHeightOffset;
264 Vector3 innerDepthOffset = Vector3.back*BalconyThickness;
265 Vector3 innerDepth = balconyDepth - innerDepthOffset;
266 var balconyInner = MeshDraft.PartialBox(innerWidth, innerDepth, innerHeight,
267 Directions.All & ~Directions.Up & ~Directions.Back);
268 balconyInner.Move(balconyCenter - innerDepthOffset/2 + innerHeightOffset/2);
269
270 Vector3 borderOrigin = origin + widthVector + balconyHeight;
271 Vector3 borderInnerOrigin = borderOrigin - innerWidthOffset;
272 var balconyBorder = Bracket(borderOrigin, -widthVector, balconyDepth,
273 borderInnerOrigin, -innerWidth, innerDepth);
274
275 var balcony = new MeshDraft();
276 balcony.Add(balconyOuter);
277 balcony.Add(balconyInner);
278 balcony.Add(balconyBorder);
279 balcony.Paint(wallColor);
280 return balcony;
281 }
282
283 private static MeshDraft Bracket(Vector3 origin, Vector3 width, Vector3 length,
284 Vector3 innerOrigin, Vector3 innerWidth, Vector3 innerLength)
285 {
286 return MeshDraft.TriangleStrip(new List<Vector3>
287 {
288 innerOrigin,
289 origin,
290 innerOrigin + innerLength,
291 origin + length,
292 innerOrigin + innerLength + innerWidth,
293 origin + length + width,
294 innerOrigin + innerWidth,
295 origin + width
296 });
297 }
298
299 protected static MeshDraft BalconyGlazed(Vector3 origin, float width, float height, Color wallColor,
300 Color frameColor, Color glassColor, Color roofColor)
301 {
302 Vector3 widthVector = Vector3.right*width;
303 Vector3 heightVector = Vector3.up*height;
304
305 Vector3 balconyHeight = Vector3.up*BalconyHeight;
306 Vector3 balconyDepth = Vector3.back*BalconyDepth;
307
308 var draft = new MeshDraft();
309 var balcony = MeshDraft.PartialBox(widthVector, balconyDepth, balconyHeight,
310 Directions.All & ~Directions.Up & ~Directions.Back);
311 balcony.FlipFaces();
312 balcony.Move(origin + widthVector/2 + balconyDepth/2 + balconyHeight/2);
313 balcony.Paint(wallColor);
314 draft.Add(balcony);
315
316 Vector3 roof0 = origin + heightVector;
317 Vector3 roof1 = roof0 + widthVector;
318 Vector3 roof2 = roof1 + balconyDepth;
319 Vector3 roof3 = roof0 + balconyDepth;
320 var roof = MeshDraft.Quad(roof0, roof1, roof2, roof3);
321 roof.Paint(roofColor);
322 draft.Add(roof);
323
324 Vector3 glassHeight = heightVector - balconyHeight;
325 Vector3 glass0 = origin + balconyHeight;
326 Vector3 glass1 = glass0 + balconyDepth;
327 Vector3 glass2 = glass1 + widthVector;
328 var glass = Windowpane(glass0, glass0 + balconyDepth + glassHeight, frameColor, glassColor);
329 glass.Add(Windowpane(glass1, glass1 + widthVector + glassHeight, frameColor, glassColor));
330 glass.Add(Windowpane(glass2, glass2 - balconyDepth + glassHeight, frameColor, glassColor));
331 draft.Add(glass);
332
333 return draft;
334 }
335
336 protected static MeshDraft Entrance(Vector3 origin, float width, float height, Color wallColor, Color doorColor)
337 {
338 Vector3 widthVector = Vector3.right*width;
339 Vector3 heightVector = Vector3.up*height;
340
341 Vector3 doorWidth = Vector3.right*EntranceDoorWidth;
342 Vector3 doorHeight = Vector3.up*EntranceDoorHeight;
343 Vector3 doorThickness = Vector3.back*EntranceDoorThickness;
344 Vector3 doorOrigin = origin + widthVector/2 - doorWidth/2;
345
346 var draft = Bracket(origin, widthVector, heightVector, doorOrigin, doorWidth, doorHeight);
347 draft.Paint(wallColor);
348
349 var doorFrame = MeshDraft.PartialBox(doorWidth, -doorThickness, doorHeight,
350 Directions.All & ~Directions.ZAxis);
351 doorFrame.Move(doorOrigin + doorWidth/2 + doorHeight/2 + doorThickness/2);
352 doorFrame.Paint(doorColor);
353 draft.Add(doorFrame);
354
355 var door = MeshDraft.Quad(doorOrigin + doorThickness, doorWidth, doorHeight);
356 door.Paint(doorColor);
357 draft.Add(door);
358 return draft;
359 }
360
361 protected static MeshDraft EntranceRoofed(Vector3 origin, float width, float height, Color wallColor,
362 Color doorColor, Color roofColor)
363 {
364 var draft = Entrance(origin, width, height, wallColor, doorColor);
365 Vector3 widthVector = Vector3.right*width;
366 Vector3 lengthVector = Vector3.forward*EntranceRoofLength;
367
368 var roof = MeshDraft.PartialBox(widthVector, lengthVector, Vector3.up*EntranceRoofHeight,
369 Directions.All & ~Directions.Forward);
370 roof.Move(origin + widthVector/2 + Vector3.up*(height - EntranceRoofHeight/2) - lengthVector/2);
371 roof.Paint(roofColor);
372 draft.Add(roof);
373 return draft;
374 }
375
376 protected static MeshDraft EntranceWindow(Vector3 origin, float width, float height, Color wallColor,
377 Color frameColor, Color glassColor)
378 {
379 var draft = Window(origin, width, height/2, EntranceWindowWidthOffset, EntranceWindowHeightOffset,
380 EntranceWindowHeightOffset, wallColor, frameColor, glassColor);
381 draft.Add(Window(origin + Vector3.up*height/2, width, height/2, EntranceWindowWidthOffset,
382 EntranceWindowHeightOffset, EntranceWindowHeightOffset, wallColor, frameColor, glassColor));
383 return draft;
384 }
385
386 protected static MeshDraft SocleWindowed(Vector3 origin, float width, float height, Color wallColor,
387 Color glassColor)
388 {
389 Vector3 widthVector = Vector3.right*width;
390 Vector3 heightVector = Vector3.up*height;
391
392 Vector3 windowWidth = Vector3.right*SocleWindowWidth;
393 Vector3 windowHeigth = Vector3.up*SocleWindowHeight;
394 Vector3 windowDepth = Vector3.forward*SocleWindowDepth;
395 Vector3 windowOrigin = origin + widthVector/2 - windowWidth/2 + Vector3.up*SocleWindowHeightOffset;
396 Vector3 windowMax = windowOrigin + windowWidth + windowHeigth;
397
398 var wall = PerforatedQuad(origin, origin + widthVector + heightVector, windowOrigin, windowMax);
399
400 var frame = MeshDraft.PartialBox(windowWidth, -windowDepth, windowHeigth,
401 Directions.All & ~Directions.ZAxis);
402 frame.Move(windowOrigin + windowWidth/2 + windowHeigth/2 + windowDepth/2);
403 wall.Add(frame);
404 wall.Paint(wallColor);
405
406 var window = MeshDraft.Quad(windowOrigin + windowDepth/2, windowWidth, windowHeigth);
407 window.Paint(glassColor);
408 wall.Add(window);
409
410 return wall;
411 }
412
413 protected static MeshDraft AtticVented(Vector3 origin, float width, float height, Color wallColor,
414 Color holeColor)
415 {
416 Vector3 widthVector = Vector3.right*width;
417 Vector3 heightVector = Vector3.up*height;
418 Vector3 center = origin + widthVector/2 + heightVector/2;
419
420 Vector3 holeOrigin = center - Vector3.right*AtticHoleWidth/2 - Vector3.up*AtticHoleHeight/2;
421 Vector3 holeWidth = Vector3.right*AtticHoleWidth;
422 Vector3 holeHeight = Vector3.up*AtticHoleHeight;
423 Vector3 holeDepth = Vector3.forward*AtticHoleDepth;
424
425 var draft = PerforatedQuad(origin, origin + widthVector + heightVector, holeOrigin,
426 holeOrigin + holeWidth + holeHeight);
427 draft.Paint(wallColor);
428
429 var hexahedron = MeshDraft.PartialBox(holeWidth, holeDepth, holeHeight, Directions.All & ~Directions.Back);
430 hexahedron.Move(center + holeDepth/2);
431 hexahedron.FlipFaces();
432 hexahedron.Paint(holeColor);
433 draft.Add(hexahedron);
434 return draft;
435 }
436 }
437
438 public class ProceduralWall : ProceduralFacadePanel
439 {
440 private Color wallColor;
441
442 public ProceduralWall(Color wallColor)
443 {
444 this.wallColor = wallColor;
445 }
446
447 public override MeshDraft GetMeshDraft()
448 {
449 var draft = MeshDraft.Quad(origin.Value, Vector3.right*width.Value, Vector3.up*height.Value);
450 draft.Paint(wallColor);
451 return draft;
452 }
453 }
454
455 public class ProceduralWindow : ProceduralFacadePanel
456 {
457 private Color wallColor;
458 private Color frameColor;
459 private Color glassColor;
460
461 public ProceduralWindow(Color wallColor, Color frameColor, Color glassColor)
462 {
463 this.wallColor = wallColor;
464 this.frameColor = frameColor;
465 this.glassColor = glassColor;
466 }
467
468 public override MeshDraft GetMeshDraft()
469 {
470 return Window(origin.Value, width.Value, height.Value, wallColor, frameColor, glassColor);
471 }
472 }
473
474 public class ProceduralBalcony : ProceduralFacadePanel
475 {
476 private Color wallColor;
477 private Color frameColor;
478 private Color glassColor;
479
480 public ProceduralBalcony(Color wallColor, Color frameColor, Color glassColor)
481 {
482 this.wallColor = wallColor;
483 this.frameColor = frameColor;
484 this.glassColor = glassColor;
485 }
486
487 public override MeshDraft GetMeshDraft()
488 {
489 return Balcony(origin.Value, width.Value, height.Value, wallColor, frameColor, glassColor);
490 }
491 }
492
493 public class ProceduralBalconyGlazed : ProceduralFacadePanel
494 {
495 private Color wallColor;
496 private Color frameColor;
497 private Color glassColor;
498 private Color roofColor;
499
500 public ProceduralBalconyGlazed(Color wallColor, Color frameColor, Color glassColor, Color roofColor)
501 {
502 this.wallColor = wallColor;
503 this.frameColor = frameColor;
504 this.glassColor = glassColor;
505 this.roofColor = roofColor;
506 }
507
508 public override MeshDraft GetMeshDraft()
509 {
510 return BalconyGlazed(origin.Value, width.Value, height.Value, wallColor, frameColor, glassColor, roofColor);
511 }
512 }
513
514 public class ProceduralEntrance : ProceduralFacadePanel
515 {
516 private Color wallColor;
517 private Color doorColor;
518
519 public ProceduralEntrance(Color wallColor, Color doorColor)
520 {
521 this.wallColor = wallColor;
522 this.doorColor = doorColor;
523 }
524
525 public override MeshDraft GetMeshDraft()
526 {
527 return Entrance(origin.Value, width.Value, height.Value, wallColor, doorColor);
528 }
529 }
530
531 public class ProceduralEntranceRoofed : ProceduralFacadePanel
532 {
533 private Color wallColor;
534 private Color doorColor;
535 private Color roofColor;
536
537 public ProceduralEntranceRoofed(Color wallColor, Color doorColor, Color roofColor)
538 {
539 this.wallColor = wallColor;
540 this.doorColor = doorColor;
541 this.roofColor = roofColor;
542 }
543
544 public override MeshDraft GetMeshDraft()
545 {
546 return EntranceRoofed(origin.Value, width.Value, height.Value, wallColor, doorColor, roofColor);
547 }
548 }
549
550 public class ProceduralEntranceWindow : ProceduralFacadePanel
551 {
552 private Color wallColor;
553 private Color frameColor;
554 private Color glassColor;
555
556 public ProceduralEntranceWindow(Color wallColor, Color frameColor, Color glassColor)
557 {
558 this.wallColor = wallColor;
559 this.frameColor = frameColor;
560 this.glassColor = glassColor;
561 }
562
563 public override MeshDraft GetMeshDraft()
564 {
565 return EntranceWindow(origin.Value, width.Value, height.Value, wallColor, frameColor, glassColor);
566 }
567 }
568
569 public class ProceduralSocle : ProceduralWall
570 {
571 public ProceduralSocle(Color wallColor) : base(wallColor)
572 {
573 }
574 }
575
576 public class ProceduralSocleWindowed : ProceduralFacadePanel
577 {
578 private Color wallColor;
579 private Color glassColor;
580
581 public ProceduralSocleWindowed(Color wallColor, Color glassColor)
582 {
583 this.wallColor = wallColor;
584 this.glassColor = glassColor;
585 }
586
587 public override MeshDraft GetMeshDraft()
588 {
589 return SocleWindowed(origin.Value, width.Value, height.Value, wallColor, glassColor);
590 }
591 }
592
593 public class ProceduralAtticVented : ProceduralFacadePanel
594 {
595 private Color wallColor;
596 private Color holeColor;
597
598 public ProceduralAtticVented(Color wallColor, Color holeColor)
599 {
600 this.wallColor = wallColor;
601 this.holeColor = holeColor;
602 }
603
604 public override MeshDraft GetMeshDraft()
605 {
606 return AtticVented(origin.Value, width.Value, height.Value, wallColor, holeColor);
607 }
608 }
609 }